﻿/*
参考资料：https://sunnycoding.cn/2018/01/11/aspnetcore-webapi-eda-part2/
总结：在进行系统架构的时候，要尽量保证较短的对象生命周期。
 */

using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Zo.Events
{
    /// <summary>
    /// 事件处理器执行程序。
    /// </summary>
    /// <remarks>
    /// 作用：为事件处理器提供了一个完整的生命周期管理机制，在这套机制中，事件处理器及其引用的对象资源可以被正常创建和正常销毁。
    /// 原因：在进行系统架构的时候，我们应该尽量保证较短的对象生命周期，以免因为状态的不一致性导致不可回滚的错误出现，这也是架构设计中的一种最佳实践。
    /// </remarks>
    public interface IEventHandlerExecutor
    {
        void RegisterHandler<TEvent, THandler>()
        where TEvent : class, IEvent
        where THandler : class, IEventHandler<TEvent>;

        void RegisterHandler(Type eventType, Type handlerType);

        bool HandlerRegistered<TEvent, THandler>()
            where TEvent : class, IEvent
            where THandler : class, IEventHandler<TEvent>;

        bool HandlerRegistered(Type eventType, Type handlerType);

        IEventResult HandleEvent(IEvent @event);

        Task HandleEventAsync(IEvent @event, CancellationToken cancellationToken = default);
    }

    public class DefaultEventHandlerExecutor : IEventHandlerExecutor
    {
        private readonly IServiceCollection _registry;
        private readonly Func<IServiceCollection, IServiceProvider> _serviceProviderFactory;
        private readonly ConcurrentDictionary<Type, List<Type>> _registrations = new ConcurrentDictionary<Type, List<Type>>();

        public DefaultEventHandlerExecutor(IServiceCollection registry, Func<IServiceCollection, IServiceProvider> serviceProviderFactory = null)
        {
            _registry = registry;
            _serviceProviderFactory = serviceProviderFactory ?? (sc => registry.BuildServiceProvider());
        }

        #region 执行事件

        public IEventResult HandleEvent(IEvent @event)
        {
            var eventType = @event.GetType();

            //var eventResults = new List<IEventResult>();
            var currentEventResult = new EventResult();

            if (_registrations.TryGetValue(eventType, out List<Type> handlerTypes) && handlerTypes?.Count > 0)
            {
                var serviceProvider = _serviceProviderFactory(_registry);
                using (var childScope = serviceProvider.CreateScope())
                {
                    foreach (var handlerType in handlerTypes)
                    {
                        var handler = (IEventHandler)childScope.ServiceProvider.GetService(handlerType);
                        if (handler.CanHandle(@event))
                        {
                            var eventResult = handler.Handle(@event);

                            if (eventResult != null)
                            {
                                if (string.IsNullOrWhiteSpace(currentEventResult.ErrorMessage))
                                {
                                    currentEventResult.ErrorCode = eventResult.ErrorCode ?? string.Empty;
                                    currentEventResult.ErrorMessage = eventResult.ErrorMessage ?? string.Empty;
                                }

                                currentEventResult.Add(eventResult);
                            }
                        }
                    }
                }
            }

            return currentEventResult;
        }

        public async Task HandleEventAsync(IEvent @event, CancellationToken cancellationToken = default)
        {
            var eventType = @event.GetType();

            if (_registrations.TryGetValue(eventType, out List<Type> handlerTypes) && handlerTypes?.Count > 0)
            {
                var serviceProvider = _serviceProviderFactory(_registry);
                using (var childScope = serviceProvider.CreateScope())
                {
                    foreach (var handlerType in handlerTypes)
                    {
                        var handler = (IEventHandler)childScope.ServiceProvider.GetService(handlerType);
                        if (handler.CanHandle(@event))
                        {
                            await handler.HandleAsync(@event, cancellationToken);
                        }
                    }
                }
            }
        }

        #endregion 执行事件

        public bool HandlerRegistered<TEvent, THandler>()
            where TEvent : class, IEvent
            where THandler : class, IEventHandler<TEvent>
        {
            return HandlerRegistered(typeof(TEvent), typeof(THandler));
        }

        public bool HandlerRegistered(Type eventType, Type handlerType)
        {
            if (_registrations.TryGetValue(eventType, out List<Type> handlerTypeList))
            {
                return handlerTypeList != null && handlerTypeList.Contains(handlerType);
            }

            return false;
        }

        public void RegisterHandler<TEvent, THandler>()
            where TEvent : class, IEvent
            where THandler : class, IEventHandler<TEvent>
        {
            RegisterHandler(typeof(TEvent), typeof(THandler));
        }

        public void RegisterHandler(Type eventType, Type handlerType)
        {
            ConcurrentDictionarySafeRegister(eventType, handlerType, _registrations);

            //瞬时模式：每次都获取一个新的实例。
            _registry.AddTransient(handlerType);
        }

        private static void ConcurrentDictionarySafeRegister<TKey, TValue>(TKey key, TValue value, ConcurrentDictionary<TKey, List<TValue>> registry)
        {
            if (registry.TryGetValue(key, out List<TValue> registryItem))
            {
                if (registryItem != null)
                {
                    if (!registryItem.Contains(value))
                    {
                        registry[key].Add(value);
                    }
                }
                else
                {
                    registry[key] = new List<TValue> { value };
                }
            }
            else
            {
                registry.TryAdd(key, new List<TValue> { value });
            }
        }
    }
}